package com.tom.baselibrary.ioc;

import android.app.Activity;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.view.View;
import android.widget.Toast;

import androidx.fragment.app.Fragment;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * Added by Tom on 2024/06/24.
 * View的注入，做到通用
 */
public class ViewUtils {

    public static void inject(Fragment fragment) {
        inject(new ViewFinder(fragment), fragment);
    }

    public static void inject(Activity activity) {
        inject(new ViewFinder(activity), activity);
    }

    // 兼容View。
    public static void inject(View view) {
        inject(new ViewFinder(view), view);
    }

    // object 是凡是需要执行的类：eg： Activity。
    public static void inject(View view, Object object) {
        inject(new ViewFinder(view), object);
    }

    // 兼容上面3个方法，做到通用。 object --> 反射需要执行的类
    // object 是反射需要执行的类（activity/fragment/view/object）
    private static void inject(ViewFinder finder, Object object) {
        injectField(finder, object); // 注入属性
        injectEvent(finder, object); // 注入事件
    }

    // 注入事件
    private static void injectEvent(ViewFinder finder, Object object) {
        // 1.获取类里面所有的方法
        Class<?> clazz = object.getClass();
        Method[] methods = clazz.getDeclaredMethods();
        // 2.获取OnClick里面的value值
        for (Method method : methods) {
            OnClick onClick = method.getAnnotation(OnClick.class); // 获取有指定注解的方法
            if (onClick != null) {
                int[] viewIds = onClick.value(); // 点击事件可配置多个view。
                for (int viewId : viewIds) {
                    // 3.findViewById 找到View
                    View view = finder.findViewById(viewId);
                    // 功能拓展，监测网络。@CheckNet
                    boolean isCheckNet = method.getAnnotation(CheckNet.class) != null;

                    if (view != null) {
                        // 4.view.onClickListener，传入方法名 、
                        view.setOnClickListener(new DeclaredOnClickListener(method, object, isCheckNet));
                    }
                }
            }
        }
    }

    // 参考源码OnClick配置，调用方法是怎么实现。DeclaredOnClickListener
    // View:: setOnClickListener(new DeclaredOnClickListener(this, handlerName)); //handlerName 处理事件的方法名。
    private static class DeclaredOnClickListener implements View.OnClickListener {
        private Method mMethod; // 方法
        private Object mObject; // Activity对象
        private boolean mIsCheckNet; // 是否检测网络
        public DeclaredOnClickListener(Method method, Object object, boolean isCheckNet) {
            this.mMethod = method;
            this.mObject = object;
            this.mIsCheckNet = isCheckNet;
        }

        @Override
        public void onClick(View v) {
            // 点击最终会调用这个方法
            if (mIsCheckNet) { // 是否需要监测网络。需要，做统一判断拦截。
                if (!networkAvailable(v.getContext())) {
                    // toast内容，最好做成可以配置。在BaseApplication中用一个方法配置。
                    Toast.makeText(v.getContext(), "您的网络不太给力!", Toast.LENGTH_SHORT).show();
                    return;
                }
            }

            // 点击最终调用的方法
            try {
                // 5.反射执行方法. 执行View的 点击事件. 可以执行private的方法。
                mMethod.setAccessible(true);
                mMethod.invoke(mObject, v); // mObject 就是Context 上下文，执行v的点击事件。public void onClick(TextView tv){}
            } catch (Exception e) {
                e.printStackTrace();
                try {
                    // 传一个空数组, 做无参方法的兼容。
                    Object[] object = new Object[]{};
                    mMethod.invoke(mObject, object); // 考虑没有传入v对象的情况。public void onClick(){}
                } catch (Exception e1) {
                    e1.printStackTrace();
                }
            } // try-catch，能避免部分crash，让app 不会闪退。onClick里面，加入代码 int i=3/0; 不会闪退。有利于用户体验。
        }
    }


    // 注入属性：遍历所有的属性
    private static void injectField(ViewFinder finder, Object object) {
        // 1.遍历类里面所有的属性。
        Class<?> clazz = object.getClass();
        // 获取所有属性（私有的 + 公有的）
        Field[] fields = clazz.getDeclaredFields();

        // 2.获取viewById里面的value值
        for (Field field : fields) {
            ViewById viewById = field.getAnnotation(ViewById.class);
            if (viewById != null) {
                // 3. findViewById找到view
                int viewId = viewById.value(); // 获取注解里面的id值。R.id.test_tv
                View view = finder.findViewById(viewId);
                if (view != null) {
                    // 4. 动态注入找到View，绑定
                    try {
                        field.setAccessible(true); // 可处理所有修饰符修饰的变量。
                        field.set(object, view);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    // 判断网络是否可用 添加 android.permission.ACCESS_NETWORK_STATE 权限。
    private static boolean networkAvailable(Context context) {
        try {
            ConnectivityManager cManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkInfo networkInfo = cManager.getActiveNetworkInfo();
            if (networkInfo != null && networkInfo.isConnected()) {
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

}
